use std::fmt;
use std::fmt::Debug;
use std::io::{Result,ErrorKind,Error};

use super::umutex::{Umutex,ScopedLock};
use super::mbox::MBox;
use super::block::{NilBlock,get_mem};
use super::mem::{Bar,MemAlloc};
use super::cpblock::CPBlock;
use super::gp::{GTP,GP};
use super::raw::Raw;
use super::ubox::UBox;
use super::layout;
use mem_macro::MemAlloc;
use fifo_macro::{Fifoer,FifoPointer};

#[derive(Debug)]
pub struct UsizeArray<const L: usize> 
    where [u8;L]: Sized {
    pub b:[usize;L]
}
pub trait UAIndex {
    fn uaindex<'a>(&'a self, idx: usize) -> &'a usize;
    fn uaindex_mut<'a>(&'a mut self, idx: usize) -> &'a mut usize;
}

impl<const L:usize> UAIndex for UsizeArray<{L}> {
    fn uaindex<'a>(&'a self, idx: usize) -> &'a usize {
        &self.b[idx]
    }
    fn uaindex_mut<'a>(&'a mut self, idx: usize) -> &'a mut usize {
        &mut self.b[idx]
    }
}

pub trait BufSized:Debug {
    const BUF_LEN:usize;
    const HEAD_LEN:usize;
    const APP_LEN:usize;
    const HEAD_START:usize;
    type BufArray:Sized+Debug+UAIndex;
    type AppArray:Sized+Debug;
}


#[derive(Debug)]
pub struct FifoConfig {}

impl BufSized for FifoConfig {
    const APP_LEN:usize = 16;
    const HEAD_LEN:usize = 64;
    const BUF_LEN:usize = (layout::ADDONEND-layout::ADDONPOST-Self::HEAD_LEN)/std::mem::size_of::<usize>()-Self::APP_LEN; 
    const HEAD_START:usize = layout::ADDONPOST;
    type BufArray = UsizeArray<{Self::BUF_LEN}>;
    type AppArray = UsizeArray<{Self::APP_LEN}>;
}

pub fn inc<L:BufSized>(a :&mut usize) -> usize {
    let r = *a%L::BUF_LEN;
    *a = (r+1_usize)%L::BUF_LEN;
    r
}

#[repr(C)]
#[derive(Debug)]
pub struct FifoPoint<L:BufSized> {
    pub sp:usize,
    pub rp:usize,
    fifo_mutex:Umutex,
    _marker : std::marker::PhantomData<L>,
}

impl<L:BufSized> FifoPoint<L>{
    pub fn set_rp(&mut self,rp:usize) {
        let _l = ScopedLock::new(&mut self.fifo_mutex);
        self.rp=rp;
    }
    pub fn atomic_inc_rp(&mut self) -> usize {
        let _l = ScopedLock::new(&mut self.fifo_mutex);
        inc::<L>(&mut self.rp)
    }
    pub fn atomic_inc_sp(&mut self) -> usize {
        let _l = ScopedLock::new(&mut self.fifo_mutex);
        inc::<L>(&mut self.sp)
    }
}
#[repr(C)]
#[derive(Debug)]
pub struct FifoData<L> 
    where L:BufSized {
    pub point:FifoPoint<L>,
    buf:L::BufArray,
    appdata:L::AppArray,
}

pub trait FifoPointer {
    fn local_len(&self) -> usize;
    fn globe_len(&self) -> usize;
    fn local_reset(&mut self);
    fn sync_globe_rp(&mut self);
    fn get_local_rp(&mut self) -> usize;
    fn get_globe_rp(&mut self) -> usize;
    fn push(&mut self,off : usize);
}

#[derive(FifoPointer)]
pub struct fifo_addon<L> 
    where L:BufSized {
    bar: MBox<Bar>,
    fifo: MBox<FifoData<L>>,
    local_rp : usize,
    _marker : std::marker::PhantomData<L>,
}

pub trait Fifoer {
    fn local_get<P>(&mut self) -> Result<Option<MBox<P>>>;
    fn get<P>(&mut self) -> Result<Option<MBox<P>>>;
    fn push<P>(&mut self, p :&UBox<P>);
    fn globe_sync(&mut self);
    fn local_reset(&mut self);
    fn local_len(&self) -> usize;
    fn globe_len(&self) -> usize;
    fn get_fifo_pointer(&mut self) -> &mut FifoPointer;
}

#[derive(MemAlloc,Fifoer)]
pub struct BFifo<L> where L:BufSized {
    pub block: CPBlock<NilBlock>,
    addon: fifo_addon<L>
}

impl<L:BufSized> BFifo<L> {
    pub fn attach(fname: String) -> Result<Self> {
        let cpb = CPBlock::<NilBlock>::attach(fname)?;
        let aon = cpb.head.as_subtype::<Bar>(layout::ADDONPOST)?;
        let fifo = cpb.head.as_subtype::<FifoData<L>>(layout::ADDONPOST+layout::BAR_SIZE)?;
        Ok(BFifo::<L>{block: cpb, addon:fifo_addon{
            bar:aon,
            fifo:fifo,
            local_rp:0usize,
            _marker : std::marker::PhantomData::<L>::default(),
        }})
    }
}

pub type Fifo = BFifo<FifoConfig>;